from typing import Any

from fastapi import (
    APIRouter,
    BackgroundTasks,
    Body,
    Depends,
    HTTPException,
    Request,
    status,
)
from fastapi.responses import RedirectResponse
from fastapi.security import OAuth2PasswordRequestForm
from fastapi_sso.sso.base import SSOBase
from fastapi_sso.sso.google import OpenID
from sqlalchemy.orm import Session

from app import crud, models, schemas
from app.api import deps
from app.core import security
from app.core.security import get_password_hash
from app.email_service.auth import send_reset_password_email

router = APIRouter()


@router.get("/{provider}/login")
async def sso_login(
    *, sso: SSOBase = Depends(deps.get_generic_sso), return_url: str
) -> Any:
    """
    用于通过 SSO 提供程序登录的终结点。调用此终结点以获取所请求提供程序的重定向链接，应用用户与提供程序之间的所有身份验证过程都将在其中发生。
    """
    return await sso.get_login_redirect(state=return_url)


@router.get("/{provider}/callback")
async def sso_callback(
    *,
    db: Session = Depends(deps.get_db),
    sso: SSOBase = Depends(deps.get_generic_sso),
    request: Request,
    provider: models.SSOProvider,
) -> Any:
    """
    提供程序在身份验证过程结束时自动调用的回调 URL。此终结点不应由客户端直接调用
    """
    sso_user: OpenID = await sso.verify_and_process(request)
    token = create_sso_user(db, provider, sso_user)
    return RedirectResponse(f"{sso.state}?token={token}")


@router.post("/sso/confirm", response_model=schemas.LoginResponse)
def get_sso_access_token(
    user: models.User = Depends(deps.get_user_after_sso_confirmation),
) -> Any:
    """
    SSO 身份验证流程生成一个令牌，该令牌作为回调终结点的深度链接重定向响应的查询参数返回。
    authssoconfirm 端点允许客户端（移动应用、网站等）通过用颁发的令牌换取用户的实际身份验证信息来获取用户的身份验证信息。
    """
    return create_login_response(user)


@router.post("/login/access-token", response_model=schemas.LoginResponse)
def login_access_token(
    db: Session = Depends(deps.get_db), form_data: OAuth2PasswordRequestForm = Depends()
) -> Any:
    """
    OAuth2 兼容令牌登录，获取访问令牌以备将来请求
    """
    user = crud.user.authenticate(
        db, email=form_data.username, password=form_data.password
    )
    if not user:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="电子邮件或密码不正确",
        )
    return create_login_response(user)


@router.post("/refresh", response_model=schemas.LoginResponse)
def refresh_token(user: models.User = Depends(deps.get_user_from_refresh_token)) -> Any:
    """
    使用刷新令牌刷新身份验证信息
    """
    return create_login_response(user)


@router.post("/login/test-token", response_model=schemas.User)
def test_token(current_user: models.User = Depends(deps.get_current_user)) -> Any:
    """
    测试访问令牌
    """
    return current_user


@router.post("/password-recovery/{email}", response_model=schemas.Msg)
def recover_password(
    background_tasks: BackgroundTasks, email: str, db: Session = Depends(deps.get_db)
) -> Any:
    """
    密码恢复
    """
    user = crud.user.get_by_email(db, email=email)

    if not user:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="系统中不存在具有此用户名的用户。",
        )
    password_reset_token = security.generate_password_reset_token(email=email)
    background_tasks.add_task(
        send_reset_password_email,
        email_to=user.email,
        email=email,
        token=password_reset_token,
    )
    return {"msg": "已发送密码恢复电子邮件"}


@router.post("/reset-password/", response_model=schemas.Msg)
def reset_password(
    token: str = Body(...),
    new_password: str = Body(...),
    db: Session = Depends(deps.get_db),
) -> Any:
    """
    重置密码
    """
    email = security.verify_password_reset_token(token)
    if not email:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED, detail="无效token"
        )
    user = crud.user.get_by_email(db, email=email)
    if not user:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="系统中不存在具有此用户名的用户.",
        )
    password_hash = get_password_hash(new_password)
    user.password_hash = password_hash
    db.add(user)
    db.commit()
    return {"msg": "密码已成功更新"}


def create_login_response(user: models.User) -> schemas.LoginResponse:
    return schemas.LoginResponse(
        access_token=security.create_access_token(user.id),
        refresh_token=security.create_refresh_token(user.id),
        # access_token = "access_token",
        # refresh_token = "refresh_token",
        token_type="bearer",
        user=user,
    )


def create_sso_user(db: Session, provider: models.Provider, openid_user: OpenID) -> str:
    user = crud.user.get_by_sso_provider_id(
        db, sso_provider_id=openid_user.id, provider=provider
    )

    if user is None:
        # 使用电子邮件验证用户是否存在
        # 当用户已经使用电子邮件或 Facebook 注册并且现在想要使用 Google 登录时，可能会发生这种情况
        user = crud.user.get_by_email(db, email=openid_user.email)
        if user is not None:
            raise HTTPException(
                status_code=status.HTTP_409_CONFLICT,
                detail=f'The user with email "{openid_user.email}" already exists in the system.',
            )
        # 创建用户
        user_in = schemas.UserCreate(
            email=openid_user.email,
            sso_provider_id=openid_user.id,
            provider=provider,
            first_name=openid_user.first_name,
            last_name=openid_user.last_name,
        )
        user = crud.user.create(db, obj_in=user_in)
    user = crud.user.update_sso_confirmation_code(db, user)
    token = security.create_sso_confirmation_token(user.sso_confirmation_code)
    return token
